import os
import re
import subprocess

class ShaderCompiler(object):
    """
    Runs an offline compilation steps for a given shader file and a given platform
    """
    
    def __init__(self):
        self.platform = None
        self.source_dir = None
        self.destination_dir = None

    def setPlatform(self, in_platform, in_source_dir, in_destination_dir):
        self.platform = in_platform
        self.source_dir = in_source_dir
        self.destination_dir = in_destination_dir

    def findWindowsFxc(self):
        self.sdk_path = os.environ.get("DXSDK_DIR");
        # need to look for the fxc exe in either the x86 or x64 paths from this root
        return self.sdk_path != None

    def findPlatformCompiler(self):
        if self.platform == "windows":
            return self.findWindowsFxc()

        return False

    def compile(self):
        """
        Find all shaders by extension, parse them for what to compile and compile them.
        """
        
        # generate the list of files to parse
        shaders = []
        for root, dirs, files in os.walk(self.source_dir):
            for file in files:
                if file.endswith(".hlsl"):
                    shaders.append( os.path.join(root, file) )
            
        # generate the compile list
        targets = []
        for file in shaders:
            targets.extend( self.parseCompileTargets(file) )

        # compile each for the target platform
        for target in targets:
            self.compileTarget( target )

    def compileTarget(self, target):
        # get the name of the file, to use for generating compiled files
        file_name_ext = os.path.basename( target[0] )
        file_name = os.path.splitext( file_name_ext )[0]

        extension = ""
        if target[1] == "vs":
            extension = "vsb"
        elif target[1] == "ps":
            extension = "psb"
        
        output = os.path.join(self.destination_dir, file_name + "_" + target[2] + "." + extension)

        # call the compiler based on platform
        if self.platform == "windows":
            self.compileTargetFxc(target, output)

    def compileTargetFxc(self, in_source, in_destination):
        
        # setup the command args
        target_profile = ""
        if in_source[1] == "vs":
            target_profile = "vs_5_0"
        elif in_source[1] == "ps":
            target_profile = "ps_5_0"

        command = [self.sdk_path + "Utilities/bin/x64/fxc", "/T" + target_profile, "/E" + in_source[2], "/Fo" + in_destination, in_source[0]]
        result = subprocess.call(command)
        if result == 0:
            print("Success with " + in_destination)
        else:
            print("Failure with " + in_destination)
    
    def parseCompileTargets(self, in_file_path):
        """
        Parse the file looking for commands. Each command is on a line by itself.
        """
        # parse the file looking for commands
        file_handle = open(in_file_path, "r")
        contents = file_handle.read()
        file_handle.close()

        compile_targets = []

        command_re = re.compile("//--[ \t]*(?P<stage>(\w)*)[ \t]*:[ \t]*(?P<entry>(\w)*)[ \t]*[\n\r]")
        command_match_iter = command_re.finditer(contents)
        for match in command_match_iter:
            stage = match.group("stage")
            entry = match.group("entry")
            compile_targets.append( (in_file_path, stage, entry) )
        
        return compile_targets